#ifndef _GWBASE_TEST_HARNESS_H_
#define _GWBASE_TEST_HARNESS_H_

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <string>
#include <sstream>
#include "gwbase/base/logging.h"
#include "gwbase/base/test_util.h"

namespace dicom {

extern void RunAllTests();

class Assertion {
 public:
  Assertion(const SourceLocation& loc) : loc_(loc), ok_(true) {}

  ~Assertion() {
    if (!ok_) {
      fprintf(stderr, "%s:%d: %s\n", loc_.file(), loc_.line(),
              os_.str().c_str());
      exit(1);
    }
  }

  Assertion& IsTrue(bool result, const char* msg) {
    if (!result) {
      os_ << "Assertion Failure: " << msg;
      ok_ = false;
    }
    return *this;
  };

#define BIN_OP(name, op)                                \
  template <class X, class Y>                           \
  Assertion& name(const X& x, const Y& y) {             \
    if (!(x op y)) {                                    \
      os_ << "failed: " << x << ' ' << #op << ' ' << y; \
      ok_ = false;                                      \
    }                                                   \
    return *this;                                       \
  }

  BIN_OP(IsEq, == )
  BIN_OP(IsNe, != )
  BIN_OP(IsLe, <= )
  BIN_OP(IsLt, < )
  BIN_OP(IsGe, >= )
  BIN_OP(IsGt, > )
#undef BIN_OP

  template <class T>
  Assertion& operator<<(const T& t) {
    os_ << t;
    return *this;
  }

 private:
  SourceLocation loc_;
  bool ok_;
  std::ostringstream os_;
};

#define EXPECT_TRUE(a) dicom::Assertion(HERE).IsTrue(a, #a)
#define EXPECT_Eq(a, b) dicom::Assertion(HERE).IsEq(a, b)
#define EXPECT_Ne(a, b) dicom::Assertion(HERE).IsNe(a, b)
#define EXPECT_Le(a, b) dicom::Assertion(HERE).IsLe(a, b)
#define EXPECT_Lt(a, b) dicom::Assertion(HERE).IsLt(a, b)
#define EXPECT_Ge(a, b) dicom::Assertion(HERE).IsGe(a, b)
#define EXPECT_Gt(a, b) dicom::Assertion(HERE).IsGt(a, b)

#define JOIN1(a, b) a##b
#define JOIN(a, b) JOIN1(a, b)

#define TEST(basecls, name)                                               \
  class JOIN(_TEST_, name) : public basecls {                             \
   public:                                                                \
    JOIN(_TEST_, name)() : basecls() {}                                   \
    static void _RunTest() {                                              \
      JOIN(_TEST_, name) t;                                               \
      t._Run();                                                           \
    }                                                                     \
    void _Run();                                                          \
  };                                                                      \
  const bool JOIN(_TEST_ignored, name) =                                  \
      dicom::RegisterTest(#basecls, #name, JOIN(_TEST_, name)::_RunTest); \
  void JOIN(_TEST_, name)::_Run()

typedef void (*TestFunc)(void);
extern bool RegisterTest(const char* test, const char* base, void (*func)());
}

#endif /* _GWBASE_TEST_HARNESS_H_ */
